package com.xbai.config;

import com.xbai.aop.LogAspects;
import com.xbai.aop.MathCalculator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * @author jxbai
 * @desc AOP：动态代理
 *  指在程序运行期间动态的将其代码切入到指定位置进行运行的编程方式。
 *  1、导入 aop 模块；Spring AOP：（spring-aspects）
 *  2、定义一个业务逻辑类（MathCalculator）；在业务逻辑运行的时候将日志进行打印（方法之前，方法运行结束、方法出现异常）
 *  3、定义一个日志切面类（LogAspects）：切面类里面的方法需要动态感知到MathCalculator.div运行到了哪里，然后执行
 *      通知方法：
 *          前置通知(@Before)：logStart：在目标方法运行之前运行
 *          后置通知(@After)：logEnd：在目标方法运行结束之后运行（无论方法是正常结束还是异常结束）
 *          返回通知(@AfterReturning)：logReturn：在目标方法正常返回之后运行
 *          异常通知(@AfterThrowing)：logException：在目标方法出现异常之后运行
 *          环绕通知(@Around)：动态代理，手动推进目标方法运行（joinPoint.proceed()）
 *  4、给切面类的目标方法标注何时何地运行（通知注解）
 *  5、将切面类和业务逻辑类（目标方法所在类）都加入到容器中
 *  6、必须告诉 Spring 哪个类是切面类（给切面类加上一个注解：@Aspect）
 *  7、给配置类中加 @EnableAspectJAutoProxy 开启注解的 aop 模式，相当于 xml 配置的 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
 *      在 Spring 中有很多的 @EnableXXX，都是用来开启XXX功能，用来替代xml的配置
 *
 *  三步：
 *      1、将业务逻辑组件和切面类都加入到容器中，告诉Spring哪个是切面类（@Aspect）
 *      2、在切面类上的每一个通知方法上标注通知注解，告诉Spring何时何地运行（切入点表达式）
 *      3、开启基于注解的 aop 模式：@EnableAspectJAutoProxy
 *
 * AOP 原理：【看给容器中注册了什么组件，这个组件什么时候工作，这个组件的功能是什么？】
 *      1、@EnableAspectJAutoProxy：是什么？
 *          @Import({AspectJAutoProxyRegistrar.class})：给容器中导入 AspectJAutoProxyRegistrar 组件
 *              利用 AspectJAutoProxyRegistrar 自定义给容器注册 bean
 *              internalAutoProxyCreator = AnnotationAwareAspectJAutoProxyCreator
 *         给容器中注册一个 AnnotationAwareAspectJAutoProxyCreator
 *
 *      2、AnnotationAwareAspectJAutoProxyCreator：
 *          -> AspectJAwareAdvisorAutoProxyCreator
 *              -> AbstractAdvisorAutoProxyCreator
 *                  -> AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
 *                  关注后置处理器（在 bean 初始化完成前后做事情）、自动装配BeanFactory
 *
 *  AbstractAutoProxyCreator#setBeanFactory()
 *  AbstractAutoProxyCreator#有后置处理器的逻辑
 *
 *  AbstractAdvisorAutoProxyCreator#setBeanFactory() -> initBeanFactory()
 *
 *  AnnotationAwareAspectJAutoProxyCreator#initBeanFactory()
 *
 * 流程
 *      1、传入配置类，创建 ioc 容器
 *      2、注册配置类，调用refresh()刷新容器
 *      3、registerBeanPostProcessors(beanFactory);注册 bean 的后置处理器来方便拦截 bean 的创建
 *          1、先获取 ioc 容器中已经定义了的需要创建对象的所有 BeanPostProcessor
 *          2、给容器中加其他的 BeanPostProcessor
 *          3、优先注册实现了 PriorityOrdered 接口的 BeanPostProcessor
 *          4、再给容器中注册实现了 Ordered 接口的 BeanPostProcessor
 *          5、注册没有实现优先级接口的 BeanPostProcessor
 *          6、注册 BeanPostProcessor，实际上就是创建 BeanPostProcessor 对象，保存在容器中
 *              创建 internalAutoProxyCreator 的 BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
 *              1、创建 Bean 的实例
 *              2、populateBean：给 bean 的各种属性赋值
 *              3、initializeBean：初始化 bean
 *                  1、invokeAwareMethods()：处理 Aware 接口的方法回调
 *                  2、applyBeanPostProcessorsBeforeInitialization()：应用后置处理器的 BeforeInitialization()方法
 *                  3、invokeInitMethods()：执行自定义的初始化方法
 *                  4、applyBeanPostProcessorsAfterInitialization()：执行 PostProcessorsAfterInitialization()方法
 *              4、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator) 创建成功; --> aspectJAdvisorsBuilder
 *          7、把 BeanPostProcessor 注册到 BeanFactory 中；beanFactory.addBeanPostProcessor(postProcessor)
 *  ===== 以上是创建和注册 AnnotationAwareAspectJAutoProxyCreator 的过程 ======
 *      AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor
 *      4、finishBeanFactoryInitialization(beanFactory);完成 BeanFactory初始化工作，创建剩下的单实例bean
 *          1、遍历获取容器中所有的 Bean，依次创建对象getBean(beanName);
 *              AbstractApplicationContext#finishBeanFactoryInitialization() -> DefaultListableBeanFactory#preInstantiateSingletons() -> AbstractBeanFactory#getBean() -> doGetBean()
 *          2、创建 bean
 *              【AnnotationAwareAspectJAutoProxyCreator 在所有 Bean 创建之前会有一个拦截，InstantiationAwareBeanPostProcessor 会调用就执行后置处理器postProcessBeforeInstantiation方法】
 *              1、先从缓存中获取当前 bean，如果能获取到，说明 bean 是之前被创建过的，自己使用，否则再创建；
 *                  只要创建好的 Bean 都会被缓存
 *
 *              2、AbstractAutowireCapableBeanFactory#createBean();创建 Bean；AnnotationAwareAspectJAutoProxyCreator 会在任何 Bean 创建之前尝试返回 Bean 的示例
 *                  【BeanPostProcessor 是在 Bean 对象创建完成初始化前后调用的】
 *                  【InstantiationAwareBeanPostProcessor 是在创建 Bean 实例之前先尝试用后置处理器返回对象的】
 *                  1、AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation(beanName, mbdToUse);解析 BeforeInstantiation
 *                      希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用，如果不能就继续
 *                      1、后置处理器先尝试返回对象
 *                        bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
 *                            拿到所有后置处理器，如果是InstantiationAwareBeanPostProcessor；
 *                            就执行后置处理器postProcessBeforeInstantiation方法
 *   					    if (bean != null) {
 *   						    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
 *                         }
 *                  2、doCreateBean(beanName, mbdToUse, args);真正的去创建一个 bean 示例；和3.6流程一样
 *
 * @date 2021/4/28
 */
@Configuration
@EnableAspectJAutoProxy
public class MainConfigOfAOP {

    // 将业务逻辑类（目标方法所在类）都加入到容器中
    @Bean
    public MathCalculator mathCalculator() {
        return new MathCalculator();
    }

    // 将切面类都加入到容器中
    @Bean
    public LogAspects logAspects() {
        return new LogAspects();
    }
}
